package com.gmp.thewizard;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.graphics.Point;
import android.util.Log;

public class Path {
	
	private List<Point> points;
	private boolean closed;
	private static final String TAG = "AvatarPath";
	
	public Path(){
		this.points = new ArrayList<Point>();
		this.closed = false;
	}
	
	protected boolean add(int x, int y){
		this.closed = false;
		return this.points.add(new Point(x, y));
	}
	
	protected boolean add(List<Point> path){
		this.closed = false;
		this.points.clear();
		return this.points.addAll(path);
	}
	
	protected Point del(int index){
		this.closed = false;
		return this.points.remove(index);
	}
	
	protected boolean del(Point index){
		this.closed = false;
		return this.points.remove(index);
	}
	
	protected int size(){
		return this.points.size();
	}
	
	protected void close(){
		Collections.sort(this.points, new PathComparator());
		this.closed = true;
	}
	
	/* Bresenham Algorithm */
	public boolean contains(int x, int y, int radius){
		
		if(!this.closed){
			Log.e(TAG, "Path not closed!");
			this.close();
		}
		
		for(int i = 1; i < this.points.size(); i++){
			
			int x1 = this.points.get(i - 1).x;
			int y1 = this.points.get(i - 1).y;
			int x2 = this.points.get(i).x;
			int y2 = this.points.get(i).y;
			
			if(!((x1 - radius) <= x && x <= (x2 + radius)) && !((y1 - radius) <= y && y <= (y2 + radius)))
				continue;
			
			int w = x2 - x1;
			int h = y2 - y1;
			
			int dx1 = 0;
			int dy1 = 0;
			int dx2 = 0;
			int dy2 = 0;
			
			dx1 = w < 0 ? -1 : 1;
			dy1 = h < 0 ? -1 : 1;
			dx2 = w < 0 ? -1 : 1;
			
			int longest = Math.abs(w);
			int shortest = Math.abs(h);
			
			if(!(longest > shortest)){
				longest = Math.abs(h);
				shortest = Math.abs(w);
				dy2 = h < 0 ? -1 : 1;
				dx2 = 0;
			}
			
			int numerator = longest >> 1;
			
			for(int g = 0; g <= longest; g++){
				if(((x1 - radius) <= x && x <= (x1 + radius)) && ((y1 - radius) <= y && y <= (y1 + radius))){
					return true;
				}
				numerator += shortest;
				if(!(numerator < longest)){
					numerator -= longest;
					x1 += dx1;
					y1 += dy1;
				} else {
					x1 += dx2;
					y1 += dy2;
				}
			}

		}
		return false;
	}
	
	public Point[] locate(int x, int y){
		
		if(!this.closed)
			System.exit(Log.e(TAG, "Path not closed!"));
		
		for(int i = 1; i < this.points.size(); i++){
			Point in0 = this.points.get(i - 1);
			Point in1 = this.points.get(i);

			int y1 = in0.y;
			int y2 = in1.y;
			
			// we are only checking the y-axis
			if(y1 <= y && y <= y2)
				return new Point[]{in0, in1};
		}
		return new Point[]{};
	}
	
	/* Line-Circle Intersection */
	public boolean altContains(int x, int y, int radius){
		
		if(!this.closed)
			System.exit(Log.e(TAG, "Path not closed!"));
		
		for(int i = 1; i < this.points.size(); i++){
			
			int x1 = this.points.get(i - 1).x;
			int y1 = this.points.get(i - 1).y;
			int x2 = this.points.get(i).x;
			int y2 = this.points.get(i).y;
			
			//if(!((x1 - radius) <= x && x <= (x2 + radius)) && !((y1 - radius) <= y && y <= (y2 + radius)))
			//	continue;
			
			int baX = x2 - x1;
			int baY = y2 - y1;
			int caX = x - x1;
			int caY = y - y1;
			
			int a = baX * baX + baY * baY;
			int bBy2 = baX * caX + baY * caY;
			int c = caX * caX + caY * caY - radius * radius;
			
			int pBy2 = bBy2 / a;
			int q = c / a;
			
			int disc = pBy2 * pBy2 - q;
			if(disc < 0){
				continue;
			} else {
				int tmpSqrt = (int) Math.sqrt(disc);
		        int abScalingFactor1 = -pBy2 + tmpSqrt;
		        int abScalingFactor2 = -pBy2 - tmpSqrt;
		        if((abScalingFactor1 < 0 || abScalingFactor1 > 1) && (abScalingFactor2 < 0 || abScalingFactor2 > 1)){
		        	if((abScalingFactor1 < 0 && abScalingFactor2 < 0) || (abScalingFactor1 > 1 && abScalingFactor2 > 1))
		        		continue;
		        	else
		        		return true;
		        } else {
		        	return true;
		        }
			}

		}
		return false;
	}
	
	public String toString(){
		String all = "[";
		for(int i = 0; i < this.points.size(); i++){
			all += "{" + this.points.get(i).x + ", " + this.points.get(i).y + "}";
			if(i < this.points.size() - 1)
				all += ", ";
		}
		all += "]";
		return all;
	}
}
